package com.fabula.android.timeline.XMPP;
import java.util.ArrayList;
import java.util.List;

import org.jivesoftware.smack.*;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smackx.pubsub.*;
import org.jivesoftware.smackx.pubsub.listener.ItemEventListener;
import org.xmlpull.v1.XmlPullParser;

import com.fabula.android.timeline.DashboardActivity;
import com.fabula.android.timeline.database.DatabaseHelper;
import com.fabula.android.timeline.database.TimelineDatabaseHelper;
import com.fabula.android.timeline.database.contentmanagers.ContentAdder;
import com.fabula.android.timeline.database.contentmanagers.ContentLoader;
import com.fabula.android.timeline.models.Event;
import com.fabula.android.timeline.models.Experience;
import com.fabula.android.timeline.utilities.Constants;
import com.fabula.android.timeline.utilities.xmlParser;

import android.util.Log;

/**
 * Simple class that handles subscription to the PubSub space spaces#team#1 and organizes
 * the space into a timeline.
 * 
 * @author Mads
 *
 */
public class PubSubListener<T extends Item>  implements ItemEventListener<T> {
	static void log(Object o){
		//Log.i("PUBSUB", ""+o);
	}

	static void err(Object o){
		//Log.e("PUBSUB ERROR", ""+o);
	}

	XMPPConnection connection;
	PubSubManager psm;
	LeafNode ln;
	boolean welcomeMessageDiscarded = false;
	@SuppressWarnings("rawtypes")
	
	/**
	 * Attempts to subscribe to the CroMAR pubsub space spaces#team#1
	 * using the given XMPPConnection
	 * 
	 * @param connection
	 * @param username
	 */
	public PubSubListener(XMPPConnection connection, String username){
		try {
			this.connection = connection;
			psm = new PubSubManager(connection,"pubsub.sandbox");
			ln = (LeafNode)psm.getNode("spaces#team#1");
			Subscription sub = ln.subscribe(username+"@sandbox");

			new TimelineDatabaseHelper(XMPPManager.context, Constants.ALL_TIMELINES_DATABASE_NAME);
			ContentLoader cl = new ContentLoader(XMPPManager.context);
			ContentAdder ca = new ContentAdder(XMPPManager.context);
			Experience CroMAR = cl.getExperienceById(Constants.CroMAR_TIMELINE);
			new DatabaseHelper(XMPPManager.context, "CroMAR.db");
			int events = 0;
			if(CroMAR!=null){
				try{
					CroMAR.setEvents(cl.LoadAllEventsFromDatabase());
					events = CroMAR.getEvents().size();
				}catch(Exception e){
					e.printStackTrace();
				}
			}
			ca.addExperienceToTimelineContentProvider(new Experience(Constants.CroMAR_TIMELINE, "CroMAR", false, DashboardActivity.creator));
			List<Item> list = ln.getItems(sub.getId());
			ArrayList<Event> eventList = new ArrayList<Event>();
			
			for(Item item: list){
				Event event = null;
				if(item instanceof PayloadItem)
					if((event = xmlParser.parseXML(((PayloadItem)item).toXML()))!=null)
						eventList.add(event);
			}

			log("List has "+eventList.size()+" entries and i've already got "+events);
			if(eventList.size()>events){
				for(;events<eventList.size();events++){
					Event event= eventList.get(events);
					if(event==null)continue;
					if(event!=null)XMPPManager.putEventPresumeOpenDB(event);
				}
			}

			ln.addItemEventListener(this);
		} catch (XMPPException e) {
			e.printStackTrace();
		}

	}
	@SuppressWarnings("rawtypes")
	/**
	 * Inserts a published item into the CroMAR timeline
	 */
	public void handlePublishedItems(ItemPublishEvent<T> items) {
		if(!welcomeMessageDiscarded){
			/*
			 * Hack to avoid handling the automatic welcome message as an actual CroMAR event
			 */
			welcomeMessageDiscarded = true;
			return;
		}
		for(Item item: items.getItems()){
			if(item instanceof PayloadItem){
				PayloadItem pi = (PayloadItem)item;
				log(pi.getPayload().toXML());
				Event event = xmlParser.parseXML(pi.getPayload().toXML());
				if(event!=null)XMPPManager.putEvent(event);
			}
			else log(item);
		}
	}
}

/**
 * Provides proper XML parsing for asmack, since Android completely ignores some class
 * information for imported libraries and can't see the one present in asmack.
 */
class ItemProvider implements PacketExtensionProvider {
	public PacketExtension parseExtension(XmlPullParser parser)
			throws Exception {
		String id = parser.getAttributeValue(null, "id");
		String elem = parser.getName();

		int tag = parser.next();

		if (tag == XmlPullParser.END_TAG) {
			return new Item(id);
		} else {
			String payloadElemName = parser.getName();
			String payloadNS = parser.getNamespace();

			if (ProviderManager.getInstance().getExtensionProvider(
					payloadElemName, payloadNS) == null) {
				StringBuilder payloadText = new StringBuilder();
				boolean degenerated = false;

				while (true) {
					if (tag == XmlPullParser.END_TAG
							&& parser.getName().equals(elem))
						break;

					if (parser.getEventType() == XmlPullParser.START_TAG) {
						payloadText.append("<").append(parser.getName());
						int n = parser.getAttributeCount();
						for (int i = 0; i < n; i++) {
							payloadText.append(" ").append(
									parser.getAttributeName(i)).append("=\"")
									.append(parser.getAttributeValue(i))
									.append("\"");
						}
						if (parser.isEmptyElementTag()) {
							payloadText.append("/>");
							degenerated = true;
						} else
							payloadText.append(">");
					} else if (parser.getEventType() == XmlPullParser.END_TAG) {
						if (degenerated)
							degenerated = false;
						else
							payloadText.append("</").append(parser.getName())
							.append(">");
					} else if (parser.getEventType() == XmlPullParser.TEXT) {
						payloadText.append(parser.getText());
					}

					tag = parser.next();
				}
				return new PayloadItem<SimplePayload>(id, new SimplePayload(
						payloadElemName, payloadNS, payloadText.toString()));
			} else {
				return new PayloadItem<PacketExtension>(id, PacketParserUtils
						.parsePacketExtension(payloadElemName, payloadNS,
								parser));
			}
		}
	}

}
